/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cardgame;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import cardgame.game.*;
import cardgame.game.commands.*;
import cardgame.game.events.*;
import cardgame.game.monsters.spells.parameter.*;
import cardgame.network.*;
import cardgame.network.messages.*;
import cardgame.updater.*;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryonet.*;

/**
 *
 * @author Carl
 */
public class ServerLauncher{
    
    public ServerLauncher(int port){
        this.port = port;
        server = new Server(BUFFER_SIZE_WRITE, BUFFER_SIZE_OBJECT);
        registerClasses(server.getKryo());
        server.start();
        server.addListener(new Listener(){

            @Override
            public void received(final Connection connection, Object receivedMessage){
                if(receivedMessage instanceof Message_GetUpdateFiles){
                    connection.sendTCP(new Message_UpdateFiles(updateManager.getUpdateFiles()));
                }
                else if(receivedMessage instanceof Message_GetUpdateFile){
                    Message_GetUpdateFile message = (Message_GetUpdateFile) receivedMessage;
                    if((message.getIndex() >= 0) && (message.getIndex() < updateManager.getUpdateFiles().length)){
                        UpdateFile updateFile = updateManager.getUpdateFiles()[message.getIndex()];
                        try{
                            String filePath = (UpdateManager.UPDATE_FILES_DIRECTORY + updateFile.getFilePath().substring(2));
                            FileInputStream fileInputStream = new FileInputStream(filePath);
                            byte[] buffer = new byte[FILE_PART_SIZE];
                            int readBytes = 0;
                            while((readBytes = fileInputStream.read(buffer)) != -1){
                                byte[] data = Arrays.copyOf(buffer, readBytes);
                                connection.sendTCP(new Message_UpdateFilePart(data));
                            }
                            fileInputStream.close();
                        }catch(FileNotFoundException ex){
                            ex.printStackTrace();
                        }catch(IOException ex){
                            ex.printStackTrace();
                        }
                    }
                }
                else if(receivedMessage instanceof Message_ClientInitialized){
                    Message_ClientInitialized message = (Message_ClientInitialized) receivedMessage;
                    players[nextPlayerIndex] = new Player();
                    playersDecks[nextPlayerIndex] = message.getDeckData();
                    connection.sendTCP(new Message_ClientAccepted(game.getGameMode().getClass().getName(), nextPlayerIndex));
                    nextPlayerIndex++;
                    if(nextPlayerIndex == players.length){
                        game.setPlayers(players[0], players[1]);
                        for(int i=0;i<players.length;i++){
                            NetworkUtil.unserialize(players[i].getDeck().getPlainTypeSerializer(), playersDecks[i]);
                            players[i].getDeck().setOwner(players[i]);
                        }
                        game.startGame();
                        server.sendToAllTCP(new Message_GameUpdate(NetworkUtil.serialize(game)));
                    }
                }
                else if(receivedMessage instanceof Message_Command){
                    Message_Command message = (Message_Command) receivedMessage;
                    if(game.isRunning()){
                        game.executeCommand(message.getCommand());
                        server.sendToAllTCP(new Message_GameUpdate(NetworkUtil.serialize(game)));
                    }
                }
            }
        });
        game = new Game(true);
        game.setGameMode(PanPlay.GAME_MODE);
        game.addEventListener(new EventListener(){

            @Override
            public boolean preEvent(Game game, Event receivedEvent){
                return true;
            }

            @Override
            public void postEvent(Game game, Event receivedEvent){
                if(receivedEvent instanceof GameOverEvent){
                    GameOverEvent event = (GameOverEvent) receivedEvent;
                    server.sendToAllTCP(new Message_GameOver(event.getWinner().getIndex()));
                }
            }
        });
        try{
            server.bind(port, port + 1);
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }
    public static final int BUFFER_SIZE_WRITE = 10000000;
    public static final int BUFFER_SIZE_OBJECT = 10000000;
    public static final int FILE_PART_SIZE = 10000;
    private int port;
    private Server server;
    private UpdateManager updateManager = new UpdateManager();
    private Game game;
    private Player[] players = new Player[2];
    private byte[][] playersDecks = new byte[2][];
    private int nextPlayerIndex;

    public int getPort(){
        return port;
    }
    
    public static int generatePort_Free(){
        int port = 33899;
        do{
            port++;
        }while(!NetworkUtil.isPortAvailable(port));
        return port;
    }
    
    public static int generatePort_Random(){
        return (33900 + ((int) (Math.random() * 100)));
    }
    
    public static void registerClasses(Kryo kryo){
        Class[] classes = new Class[]{
            byte[].class,
            int[].class,
            Message_ClientAccepted.class,
            Message_ClientInitialized.class,
            Message_Command.class,
                Command.class,
                    AddManaCommand.class,
                    AttackMonsterCommand.class,
                    AttackPlayerCommand.class,
                    CastMonsterSpellCommand.class,
                        SpellParameter.class,
                        SpellParameter[].class,
                            HandCardParameter.class,
                            ManaParameter.class,
                            MonsterParameter.class,
                    CastSpellCommand.class,
                    EndTurnCommand.class,
                    SummonMonsterCommand.class,
                    ThrowingOffCommand.class,
            Message_GameOver.class,
            Message_GameUpdate.class,
            Message_GetUpdateFile.class,
            Message_GetUpdateFiles.class,
            Message_UpdateFilePart.class,
            Message_UpdateFiles.class,
                UpdateFile.class,
                UpdateFile[].class
        };
        for(Class classToRegister : classes){
            kryo.register(classToRegister);
        }
    }
}
